import java.util.StringTokenizer
import java.lang.Math;
package drymouth;
//package labeling as needed

public class SymptomSet implements Set
{
//private data
private Set myMembers;
	
//constructors
public SymptomSet(){SymptomSet(",");}
public SymptomSet(String concatonated_symptoms)
{	myMembers=new HashSet();
	//should we make this of type string or leave it generic and class
	//cast as needed
	private	StringTokenizer tokens=new StringTokenizer(concatonated_symptoms,","); //assumes , is the separator
	while (tokens.hasMoreTokens())
	{
		symptom=tokens.nextToken();
		myMembers.add(symptom);
	}
	
	
}

//accessor	
public Set getMembers()
	{return myMembers;}
	
	
//needed because implements Set
public boolean add(Object s)
	{
		if (!(s.getClass().getName()).equals("String"))
		{	throw new Exception("Must be added as String");
			return false;
		}
		return myMembers.add((String)s);
	}
	
public boolean addAll(Collection c)
	{return myMembers.addAll(c);}

public void clear()
	{myMembers.clear();}
	
public boolean contains(Object o)
	{ 
		if (!(o.getClass().getName()).equals("String"))
		{
			throw new Exception("Must be looking for String");
			return false;
		}
		
		private String s=(String)o;
	return myMembers.contains(s);
	}
	
public boolean containsAll(Collection c)
	{return myMembers.containsAll(c);}
	
public boolean equals(Object s)
	{
		if (!((o.getClass().getName()).equals("SymptomSet")))
		{
			throw new Exception("This equals does not work for non-SymptomSets");
			return false;
		}
		
	return myMembers.equals(s.getMembers());}
	
public int hashCode()
	{return myMembers.hashCode();}
	
public boolean isEmpty()
	{return myMembers.isEmpty();}
	
public Iterator iterator()
	{return myMembers.iterator();}
	
public boolean remove(Object o)
	{return myMembers.remove(o);}
	
public boolean removeAll(Collection c)
	{return myMembers.removeAll(c);}
	
public boolean retainAll(Collection c)
	{return myMembers.retainAll(c);}

public int size()
	{return myMembers.size();}
	
public Object[] toArray()
	{return myMembers.toArray();}

public Object[] toArray(Object[] a)
	{return myMembers.toArray(a);}
	

//to be used in computation	
public int getNumberInCommon(SymptomSet s)
	{	
		//returns the number of members s has in common with me
		private Iterator iter=iterator();
		private Set otherMembers=s.getMembers();
		private int amount=0;
		while (iter.hasNext())
		{
			if (otherMembers.contains(iter.next()))
				++amount;
		}
		return amount;
		
	}
	
	


public double getSimilarity(SymptomSet s)
	{
		//returns the "similarity score" between me and another symptom set
		//defined as the number of symptoms we have in common divided by the size of the result of
		//merging the two member sets.
		
		private double similarity=0;
		private int numInCommon=getNumberInCommon(s);
		private Set mergedSymptoms= new HashSet();
		mergedSymptoms.addAll(myMembers);
		mergedSymptoms.addAll(s.getMembers());
		/* we will revise this section now to reflect a p-value of a hypergeometric test
		similarity=numInCommon/(mergedSymptoms.size()+0.0); //0.0 to make the denom a double
		return similarity;
		
		//Note: defining similarity in this way makes it symmetric, which 
		//is something we value
		*/
		private int m=(this.getMembers()).size(); //desirable members are members
		//that are THIS symptom's members
		
		private int N=m+(s.getMembers()).size();
		//we want the sum of selecting the number we have selected numInCommon up
		//to the total population size (mergedSymptoms size)
		private double logsimilarity=0;
		for (int i=numInCommon; i<=mergedSymptoms.size();++i)
		{
			logsimilarity+=Math.log(factorial(m))-Math.log(factorial(i)-Math.log(factorial(m-i)); //first choose function
			logsimilarity+=Math.log(factorial(N-m))-Math.log(factorial(n-i)-Math.log(factorial(N-m-(n-i)));//second choose function
			logsimilarity-=Math.log(factorial(N))-Math.log(factorial(n)-Math.log(factorial(N-n)); //final choose
		}
		
		//now we have calculated the log similarity.  We need to unlog for the pvalue of similarity.  low is good.
		
		similarity=Math.E^(logsimilarity);
		return similarity;
	}
	
//redefine from the Object class (from which we inherit)
public String toString()
	{
		//returns a string of the members, one per line
		private String allMembers="";
		private Iterator iter=myMembers.iterator();
		while (iter.hasNext())
		{
			allMembers+=iter.next();
		}
		return allMembers;
		
		
	}
	

private int factorial(int n)
{
	if (n==0)
		return 1;
	return n*(factorial(n-1));
}

}
